static GQuark quark_password_hint = 0;
+enum
+{
+ TEXT_HANDLE_CURSOR,
+ TEXT_HANDLE_SELECTION_BOUND,
+ TEXT_HANDLE_N_HANDLES
+};
+
typedef struct _GtkTextPasswordHint GtkTextPasswordHint;
typedef struct _GtkTextPrivate GtkTextPrivate;
char *im_module;
GtkWidget *emoji_completion;
- GtkTextHandle *text_handle;
+ GtkTextHandle *text_handles[TEXT_HANDLE_N_HANDLES];
GtkWidget *selection_bubble;
guint selection_bubble_timeout_id;
guint selection_handle_dragged : 1;
guint populate_all : 1;
guint propagate_text_width : 1;
+ guint text_handles_enabled : 1;
};
struct _GtkTextPasswordHint
/* GtkTextHandle handlers */
static void gtk_text_handle_drag_started (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
GtkText *self);
static void gtk_text_handle_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
int x,
int y,
GtkText *self);
static void gtk_text_handle_drag_finished (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
GtkText *self);
/* Internal routines
static void gtk_text_update_clipboard_actions (GtkText *self);
static void gtk_text_update_emoji_action (GtkText *self);
+static void gtk_text_update_handles (GtkText *self);
static void gtk_text_activate_clipboard_cut (GtkWidget *widget,
const char *action_name,
}
}
+static void
+gtk_text_ensure_text_handles (GtkText *self)
+{
+ GtkTextPrivate *priv = gtk_text_get_instance_private (self);
+ int i;
+
+ for (i = 0; i < TEXT_HANDLE_N_HANDLES; i++)
+ {
+ if (priv->text_handles[i])
+ continue;
+ priv->text_handles[i] = gtk_text_handle_new (GTK_WIDGET (self));
+ g_signal_connect (priv->text_handles[i], "drag-started",
+ G_CALLBACK (gtk_text_handle_drag_started), self);
+ g_signal_connect (priv->text_handles[i], "handle-dragged",
+ G_CALLBACK (gtk_text_handle_dragged), self);
+ g_signal_connect (priv->text_handles[i], "drag-finished",
+ G_CALLBACK (gtk_text_handle_drag_finished), self);
+ }
+}
+
static void
gtk_text_init (GtkText *self)
{
g_clear_pointer (&priv->selection_bubble, gtk_widget_unparent);
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
+ g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_CURSOR], gtk_widget_unparent);
+ g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_SELECTION_BOUND], gtk_widget_unparent);
g_clear_object (&priv->extra_menu);
G_OBJECT_CLASS (gtk_text_parent_class)->dispose (object);
g_clear_object (&priv->cached_layout);
g_clear_object (&priv->im_context);
g_clear_pointer (&priv->magnifier_popover, gtk_widget_destroy);
- g_clear_object (&priv->text_handle);
g_free (priv->im_module);
g_clear_pointer (&priv->placeholder, gtk_widget_unparent);
gtk_widget_show (priv->magnifier);
}
-static void
-gtk_text_ensure_text_handles (GtkText *self)
-{
- GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-
- if (priv->text_handle)
- return;
-
- priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (self));
- g_signal_connect (priv->text_handle, "drag-started",
- G_CALLBACK (gtk_text_handle_drag_started), self);
- g_signal_connect (priv->text_handle, "handle-dragged",
- G_CALLBACK (gtk_text_handle_dragged), self);
- g_signal_connect (priv->text_handle, "drag-finished",
- G_CALLBACK (gtk_text_handle_drag_finished), self);
-}
-
static void
begin_change (GtkText *self)
{
GtkText *self = GTK_TEXT (widget);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- if (priv->text_handle)
- _gtk_text_handle_set_mode (priv->text_handle,
- GTK_TEXT_HANDLE_MODE_NONE);
-
+ priv->text_handles_enabled = FALSE;
+ gtk_text_update_handles (self);
priv->cursor_alpha = 1.0;
GTK_WIDGET_CLASS (gtk_text_parent_class)->unmap (widget);
}
static void
-gtk_text_move_handle (GtkText *self,
- GtkTextHandlePosition pos,
- int x,
- int y,
- int height)
+gtk_text_move_handle (GtkText *self,
+ GtkTextHandle *handle,
+ int x,
+ int y,
+ int height)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
+ if (!gtk_text_handle_get_is_dragged (handle) &&
(x < 0 || x > gtk_widget_get_width (GTK_WIDGET (self))))
{
/* Hide the handle if it's not being manipulated
* and fell outside of the visible text area.
*/
- _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
+ gtk_widget_hide (GTK_WIDGET (handle));
}
else
{
rect.width = 1;
rect.height = height;
- _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
- _gtk_text_handle_set_position (priv->text_handle, pos, &rect);
- _gtk_text_handle_set_direction (priv->text_handle, pos, priv->resolved_dir);
+ gtk_text_handle_set_position (handle, &rect);
+ gtk_widget_set_direction (GTK_WIDGET (handle), priv->resolved_dir);
+ gtk_widget_show (GTK_WIDGET (handle));
}
}
}
static void
-gtk_text_update_handles (GtkText *self,
- GtkTextHandleMode mode)
+gtk_text_update_handles (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
const int text_height = gtk_widget_get_height (GTK_WIDGET (self));
int strong_x;
int cursor, bound;
- _gtk_text_handle_set_mode (priv->text_handle, mode);
+ if (!priv->text_handles_enabled)
+ {
+ if (priv->text_handles[TEXT_HANDLE_CURSOR])
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
+ if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
+ }
+ else
+ {
+ gtk_text_ensure_text_handles (self);
+ gtk_text_get_cursor_locations (self, &strong_x, NULL);
+ cursor = strong_x - priv->scroll_offset;
- gtk_text_get_cursor_locations (self, &strong_x, NULL);
- cursor = strong_x - priv->scroll_offset;
+ if (priv->selection_bound != priv->current_pos)
+ {
+ int start, end;
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
- {
- int start, end;
+ bound = gtk_text_get_selection_bound_location (self) - priv->scroll_offset;
- bound = gtk_text_get_selection_bound_location (self) - priv->scroll_offset;
+ if (priv->selection_bound > priv->current_pos)
+ {
+ start = cursor;
+ end = bound;
+ }
+ else
+ {
+ start = bound;
+ end = cursor;
+ }
- if (priv->selection_bound > priv->current_pos)
- {
- start = cursor;
- end = bound;
+ /* Update start selection bound */
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
+ GTK_TEXT_HANDLE_ROLE_SELECTION_END);
+ gtk_text_move_handle (self,
+ priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
+ end, 0, text_height);
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
+ GTK_TEXT_HANDLE_ROLE_SELECTION_START);
+ gtk_text_move_handle (self,
+ priv->text_handles[TEXT_HANDLE_CURSOR],
+ start, 0, text_height);
}
else
{
- start = bound;
- end = cursor;
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
+ GTK_TEXT_HANDLE_ROLE_CURSOR);
+ gtk_text_move_handle (self,
+ priv->text_handles[TEXT_HANDLE_CURSOR],
+ cursor, 0, text_height);
}
-
- /* Update start selection bound */
- gtk_text_move_handle (self, GTK_TEXT_HANDLE_POSITION_SELECTION_START,
- start, 0, text_height);
- gtk_text_move_handle (self, GTK_TEXT_HANDLE_POSITION_SELECTION_END,
- end, 0, text_height);
}
- else
- gtk_text_move_handle (self, GTK_TEXT_HANDLE_POSITION_CURSOR,
- cursor, 0, text_height);
}
static void
if (chooser)
gtk_native_check_resize (GTK_NATIVE (chooser));
- if (priv->text_handle)
- {
- GtkTextHandleMode handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
-
- if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_update_handles (self, handle_mode);
- }
+ gtk_text_update_handles (self);
if (priv->emoji_completion)
gtk_native_check_resize (GTK_NATIVE (priv->emoji_completion));
if (priv->selection_bubble)
gtk_native_check_resize (GTK_NATIVE (priv->selection_bubble));
+
+ if (priv->text_handles[TEXT_HANDLE_CURSOR])
+ gtk_native_check_resize (GTK_NATIVE (priv->text_handles[TEXT_HANDLE_CURSOR]));
+
+ if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
+ gtk_native_check_resize (GTK_NATIVE (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
}
static void
else if (button == GDK_BUTTON_PRIMARY)
{
gboolean have_selection;
- GtkTextHandleMode mode;
gboolean is_touchscreen, extend_selection;
GdkDevice *source;
guint state;
is_touchscreen = gtk_simulate_touchscreen () ||
gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
- if (!is_touchscreen)
- mode = GTK_TEXT_HANDLE_MODE_NONE;
- else if (have_selection)
- mode = GTK_TEXT_HANDLE_MODE_SELECTION;
- else
- mode = GTK_TEXT_HANDLE_MODE_CURSOR;
-
- if (is_touchscreen)
- gtk_text_ensure_text_handles (self);
+ priv->text_handles_enabled = is_touchscreen;
priv->in_drag = FALSE;
priv->select_words = FALSE;
case 2:
priv->select_words = TRUE;
gtk_text_select_word (self);
- if (is_touchscreen)
- mode = GTK_TEXT_HANDLE_MODE_SELECTION;
break;
case 3:
priv->select_lines = TRUE;
gtk_text_select_line (self);
- if (is_touchscreen)
- mode = GTK_TEXT_HANDLE_MODE_SELECTION;
break;
default:
gtk_gesture_set_state (priv->drag_gesture,
GTK_EVENT_SEQUENCE_CLAIMED);
- if (priv->text_handle)
- gtk_text_update_handles (self, mode);
+ gtk_text_update_handles (self);
}
if (n_press >= 3)
if (gtk_simulate_touchscreen () ||
input_source == GDK_SOURCE_TOUCHSCREEN)
{
- gtk_text_ensure_text_handles (self);
- gtk_text_update_handles (self,
- (priv->current_pos == priv->selection_bound) ?
- GTK_TEXT_HANDLE_MODE_CURSOR :
- GTK_TEXT_HANDLE_MODE_SELECTION);
+ priv->text_handles_enabled = TRUE;
+ gtk_text_update_handles (self);
gtk_text_show_magnifier (self, x - priv->scroll_offset, y);
}
}
GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- gboolean in_drag, is_touchscreen;
+ gboolean in_drag;
GdkEventSequence *sequence;
- GdkEvent *event;
- GdkDevice *source;
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
in_drag = priv->in_drag;
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
return;
- event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
- source = gdk_event_get_source_device (event);
- is_touchscreen = gtk_simulate_touchscreen () ||
- gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
-
if (in_drag)
{
int tmp_pos = gtk_text_find_position (self, priv->drag_start_x);
gtk_text_set_selection_bounds (self, tmp_pos, tmp_pos);
}
- if (is_touchscreen && priv->selection_bound != priv->current_pos)
- gtk_text_update_handles (self, GTK_TEXT_HANDLE_MODE_CURSOR);
+ gtk_text_update_handles (self);
gtk_text_update_primary_selection (self);
}
gtk_text_selection_bubble_popup_unset (self);
- if (priv->text_handle)
- _gtk_text_handle_set_mode (priv->text_handle,
- GTK_TEXT_HANDLE_MODE_NONE);
+ priv->text_handles_enabled = FALSE;
+ gtk_text_update_handles (self);
if (keyval == GDK_KEY_Return ||
keyval == GDK_KEY_KP_Enter ||
gtk_text_selection_bubble_popup_unset (self);
- if (priv->text_handle)
- _gtk_text_handle_set_mode (priv->text_handle,
- GTK_TEXT_HANDLE_MODE_NONE);
+ priv->text_handles_enabled = FALSE;
+ gtk_text_update_handles (self);
gtk_widget_queue_draw (widget);
gtk_text_selection_bubble_popup_unset (self);
- if (priv->text_handle)
- {
- GtkTextHandleMode handle_mode;
-
- handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
-
- if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_update_handles (self, GTK_TEXT_HANDLE_MODE_CURSOR);
- }
+ gtk_text_update_handles (self);
}
static void
else
gtk_widget_error_bell (GTK_WIDGET (self));
- if (priv->text_handle)
- {
- GtkTextHandleMode handle_mode;
-
- handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
-
- if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_update_handles (self, GTK_TEXT_HANDLE_MODE_CURSOR);
- }
+ gtk_text_update_handles (self);
}
static void
static void
gtk_text_recompute (GtkText *self)
{
- GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GtkTextHandleMode handle_mode;
-
gtk_text_reset_layout (self);
gtk_text_check_cursor_blink (self);
update_im_cursor_location (self);
- if (priv->text_handle)
- {
- handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
-
- if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_update_handles (self, handle_mode);
- }
+ gtk_text_update_handles (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
-gtk_text_handle_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- int x,
- int y,
- GtkText *self)
+gtk_text_handle_dragged (GtkTextHandle *handle,
+ int x,
+ int y,
+ GtkText *self)
{
- int cursor_pos, selection_bound_pos, tmp_pos;
+ int cursor_pos, selection_bound_pos, tmp_pos, *old_pos;
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GtkTextHandleMode mode;
- int *min, *max;
gtk_text_selection_bubble_popup_unset (self);
cursor_pos = priv->current_pos;
selection_bound_pos = priv->selection_bound;
- mode = _gtk_text_handle_get_mode (handle);
tmp_pos = gtk_text_find_position (self, x + priv->scroll_offset);
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
- cursor_pos >= selection_bound_pos)
- {
- max = &cursor_pos;
- min = &selection_bound_pos;
- }
- else
- {
- max = &selection_bound_pos;
- min = &cursor_pos;
- }
-
- if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
{
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+ /* Avoid running past the other handle in selection mode */
+ if (tmp_pos >= selection_bound_pos &&
+ gtk_widget_is_visible (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])))
{
- int min_pos;
-
- min_pos = MAX (*min + 1, 0);
- tmp_pos = MAX (tmp_pos, min_pos);
+ tmp_pos = selection_bound_pos - 1;
}
- *max = tmp_pos;
+ old_pos = &cursor_pos;
}
- else
+ else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
{
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
- {
- int max_pos;
+ /* Avoid running past the other handle */
+ if (tmp_pos <= cursor_pos)
+ tmp_pos = cursor_pos + 1;
- max_pos = *max - 1;
- *min = MIN (tmp_pos, max_pos);
- }
+ old_pos = &selection_bound_pos;
}
+ else
+ g_assert_not_reached ();
- if (cursor_pos != priv->current_pos ||
- selection_bound_pos != priv->selection_bound)
+ if (tmp_pos != *old_pos)
{
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- {
- priv->cursor_handle_dragged = TRUE;
- gtk_text_set_positions (self, cursor_pos, cursor_pos);
- }
+ *old_pos = tmp_pos;
+
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR] &&
+ !gtk_widget_is_visible (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])))
+ gtk_text_set_positions (self, cursor_pos, cursor_pos);
else
- {
- priv->selection_handle_dragged = TRUE;
- gtk_text_set_positions (self, cursor_pos, selection_bound_pos);
- }
+ gtk_text_set_positions (self, cursor_pos, selection_bound_pos);
+
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
+ priv->cursor_handle_dragged = TRUE;
+ else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
+ priv->selection_handle_dragged = TRUE;
- gtk_text_update_handles (self, mode);
+ gtk_text_update_handles (self);
}
gtk_text_show_magnifier (self, x, y);
}
static void
-gtk_text_handle_drag_started (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkText *self)
+gtk_text_handle_drag_started (GtkTextHandle *handle,
+ GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
}
static void
-gtk_text_handle_drag_finished (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkText *self)
+gtk_text_handle_drag_finished (GtkTextHandle *handle,
+ GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
if (g_get_monotonic_time() - priv->handle_place_time < double_click_time * 1000)
{
gtk_text_select_word (self);
- gtk_text_update_handles (self, GTK_TEXT_HANDLE_MODE_SELECTION);
+ gtk_text_update_handles (self);
}
else
gtk_text_selection_bubble_popup_set (self);
gtk_text_get_is_selection_handle_dragged (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GtkTextHandlePosition pos;
-
- if (!priv->text_handle)
- return FALSE;
-
- if (_gtk_text_handle_get_mode (priv->text_handle) != GTK_TEXT_HANDLE_MODE_SELECTION)
- return FALSE;
+ GtkTextHandle *handle;
if (priv->current_pos >= priv->selection_bound)
- pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
+ handle = priv->text_handles[TEXT_HANDLE_CURSOR];
else
- pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
+ handle = priv->text_handles[TEXT_HANDLE_SELECTION_BOUND];
- return _gtk_text_handle_get_is_dragged (priv->text_handle, pos);
+ return handle && gtk_text_handle_get_is_dragged (handle);
}
static void
int min_offset, max_offset;
int strong_x, weak_x;
int strong_xoffset, weak_xoffset;
- GtkTextHandleMode handle_mode;
if (!gtk_widget_get_realized (GTK_WIDGET (self)))
return;
g_object_notify_by_pspec (G_OBJECT (self), text_props[PROP_SCROLL_OFFSET]);
- if (priv->text_handle)
- {
- handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
-
- if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_update_handles (self, handle_mode);
- }
+ gtk_text_update_handles (self);
}
static int
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
gboolean visible;
- GtkTextHandle *handle;
- GtkTextHandleMode mode;
visible = gtk_widget_get_visible (popover);
-
- handle = priv->text_handle;
- mode = _gtk_text_handle_get_mode (handle);
-
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- {
- _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_CURSOR, !visible);
- }
- else if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
- {
- _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, !visible);
- _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, !visible);
- }
+ priv->text_handles_enabled = !visible;
+ gtk_text_update_handles (self);
}
static void
#include "gtkwindowprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkwidgetprivate.h"
-#include "gtkgizmoprivate.h"
#include "gtkrendericonprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtkintl.h"
#include <gtk/gtk.h>
typedef struct _GtkTextHandlePrivate GtkTextHandlePrivate;
-typedef struct _HandleWindow HandleWindow;
enum {
DRAG_STARTED,
LAST_SIGNAL
};
-enum {
- PROP_0,
- PROP_PARENT
-};
-
-struct _HandleWindow
+struct _GtkTextHandlePrivate
{
- GtkWidget *widget;
+ GtkWidget *parent;
+ GdkSurface *surface;
+ GskRenderer *renderer;
+
GdkRectangle pointing_to;
GtkBorder border;
gint dx;
gint dy;
- GtkTextDirection dir;
+ guint role : 2;
guint dragged : 1;
guint mode_visible : 1;
guint user_visible : 1;
guint has_point : 1;
};
-struct _GtkTextHandlePrivate
+struct _GtkTextHandle
{
- HandleWindow windows[2];
- GtkWidget *toplevel;
- GtkWidget *parent;
- GtkScrollable *parent_scrollable;
- GtkAdjustment *vadj;
- GtkAdjustment *hadj;
- guint hierarchy_changed_id;
- guint scrollable_notify_id;
- guint mode : 2;
+ GtkWidget parent_instance;
};
-G_DEFINE_TYPE_WITH_PRIVATE (GtkTextHandle, _gtk_text_handle, G_TYPE_OBJECT)
+static void gtk_text_handle_native_interface_init (GtkNativeInterface *iface);
-static guint signals[LAST_SIGNAL] = { 0 };
+G_DEFINE_TYPE_WITH_CODE (GtkTextHandle, gtk_text_handle, GTK_TYPE_WIDGET,
+ G_ADD_PRIVATE (GtkTextHandle)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE,
+ gtk_text_handle_native_interface_init))
-static void _gtk_text_handle_update (GtkTextHandle *handle,
- GtkTextHandlePosition pos);
+static guint signals[LAST_SIGNAL] = { 0 };
-static void
-_gtk_text_handle_get_size (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- gint *width,
- gint *height)
+static GdkSurface *
+gtk_text_handle_native_get_surface (GtkNative *native)
{
- GtkTextHandlePrivate *priv = handle->priv;
- GtkWidget *widget = priv->windows[pos].widget;
- GtkStyleContext *context;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (native);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- context = gtk_widget_get_style_context (widget);
-
- *width = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_MIN_WIDTH), 100);
- *height = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_MIN_HEIGHT), 100);
+ return priv->surface;
}
-static gint
-_text_handle_pos_from_widget (GtkTextHandle *handle,
- GtkWidget *widget)
+static GskRenderer *
+gtk_text_handle_native_get_renderer (GtkNative *native)
{
- GtkTextHandlePrivate *priv = handle->priv;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (native);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- if (widget == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget)
- return GTK_TEXT_HANDLE_POSITION_SELECTION_START;
- else if (widget == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget)
- return GTK_TEXT_HANDLE_POSITION_SELECTION_END;
- else
- return -1;
+ return priv->renderer;
}
static void
-handle_drag_begin (GtkGestureDrag *gesture,
- gdouble x,
- gdouble y,
- GtkTextHandle *handle)
+gtk_text_handle_native_get_surface_transform (GtkNative *native,
+ int *x,
+ int *y)
{
- GtkTextHandlePrivate *priv = handle->priv;
- GtkWidget *widget;
- gint pos;
-
- widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- pos = _text_handle_pos_from_widget (handle, widget);
-
- if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
- priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- x -= gtk_widget_get_width (widget) / 2;
- else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
- priv->windows[pos].dir == GTK_TEXT_DIR_RTL) ||
- (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
- priv->windows[pos].dir != GTK_TEXT_DIR_RTL))
- x -= gtk_widget_get_width (widget);
-
- y += priv->windows[pos].border.top / 2;
-
- priv->windows[pos].dx = x;
- priv->windows[pos].dy = y;
- priv->windows[pos].dragged = TRUE;
- g_signal_emit (handle, signals[DRAG_STARTED], 0, pos);
+ GtkCssStyle *style;
+
+ style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (native)));
+ *x = _gtk_css_number_value_get (style->size->margin_left, 100) +
+ _gtk_css_number_value_get (style->border->border_left_width, 100) +
+ _gtk_css_number_value_get (style->size->padding_left, 100);
+ *y = _gtk_css_number_value_get (style->size->margin_top, 100) +
+ _gtk_css_number_value_get (style->border->border_top_width, 100) +
+ _gtk_css_number_value_get (style->size->padding_top, 100);
}
static void
-handle_drag_update (GtkGestureDrag *gesture,
- gdouble offset_x,
- gdouble offset_y,
- GtkTextHandle *handle)
+gtk_text_handle_get_size (GtkTextHandle *handle,
+ gint *width,
+ gint *height)
{
- GtkTextHandlePrivate *priv = handle->priv;
- gdouble start_x, start_y;
- gint pos, x, y;
-
- pos = _text_handle_pos_from_widget (handle,
- gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
- gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
-
- gtk_widget_translate_coordinates (priv->windows[pos].widget, priv->parent,
- start_x + offset_x - priv->windows[pos].dx,
- start_y + offset_y - priv->windows[pos].dy,
- &x, &y);
- g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
-}
+ GtkWidget *widget = GTK_WIDGET (handle);
+ GtkStyleContext *context;
-static void
-handle_drag_end (GtkGestureDrag *gesture,
- gdouble offset_x,
- gdouble offset_y,
- GtkTextHandle *handle)
-{
- GtkTextHandlePrivate *priv = handle->priv;
- gint pos;
+ context = gtk_widget_get_style_context (widget);
- pos = _text_handle_pos_from_widget (handle,
- gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
- g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
- priv->windows[pos].dragged = FALSE;
+ if (width)
+ *width = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_MIN_WIDTH), 100);
+ if (height)
+ *height = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_MIN_HEIGHT), 100);
}
static void
-snapshot_func (GtkGizmo *gizmo,
- GtkSnapshot *snapshot)
+gtk_text_handle_get_padding (GtkTextHandle *handle,
+ GtkBorder *border)
{
- GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (gizmo)));
-
- gtk_css_style_snapshot_icon (style,
- snapshot,
- gtk_widget_get_width (GTK_WIDGET (gizmo)),
- gtk_widget_get_height (GTK_WIDGET (gizmo)));
-}
-
-static GtkWidget *
-_gtk_text_handle_ensure_widget (GtkTextHandle *handle,
- GtkTextHandlePosition pos)
-{
- GtkTextHandlePrivate *priv;
-
- priv = handle->priv;
+ GtkWidget *widget = GTK_WIDGET (handle);
+ GtkStyleContext *context;
- if (!priv->windows[pos].widget)
- {
- GtkWidget *widget, *window;
- GtkEventController *controller;
-
- widget = gtk_gizmo_new (I_("cursor-handle"), NULL, NULL, snapshot_func, NULL);
-
- gtk_widget_set_direction (widget, priv->windows[pos].dir);
-
- controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
- g_signal_connect (controller, "drag-begin",
- G_CALLBACK (handle_drag_begin), handle);
- g_signal_connect (controller, "drag-update",
- G_CALLBACK (handle_drag_update), handle);
- g_signal_connect (controller, "drag-end",
- G_CALLBACK (handle_drag_end), handle);
- gtk_widget_add_controller (widget, controller);
-
- priv->windows[pos].widget = g_object_ref_sink (widget);
- priv->toplevel = window = gtk_widget_get_ancestor (priv->parent, GTK_TYPE_WINDOW);
- _gtk_window_add_popover (GTK_WINDOW (window), widget, priv->parent, FALSE);
-
- if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
- {
- gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_BOTTOM);
- if (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_INSERTION_CURSOR);
- }
- else
- gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_TOP);
- }
+ context = gtk_widget_get_style_context (widget);
- return priv->windows[pos].widget;
+ border->left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_LEFT), 100);
+ border->right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_RIGHT), 100);
+ border->top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_TOP), 100);
+ border->bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_BOTTOM), 100);
}
static void
-_handle_update_child_visible (GtkTextHandle *handle,
- GtkTextHandlePosition pos)
+gtk_text_handle_present_surface (GtkTextHandle *handle)
{
- HandleWindow *handle_window;
- GtkTextHandlePrivate *priv;
- cairo_rectangle_int_t rect;
- GtkAllocation allocation;
- GtkWidget *parent;
-
- priv = handle->priv;
- handle_window = &priv->windows[pos];
-
- if (!priv->parent_scrollable)
- {
- gtk_widget_set_child_visible (handle_window->widget, TRUE);
- return;
- }
-
- parent = gtk_widget_get_parent (GTK_WIDGET (priv->parent_scrollable));
- rect = handle_window->pointing_to;
-
- gtk_widget_translate_coordinates (priv->parent, parent,
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
+ GtkWidget *widget = GTK_WIDGET (handle);
+ GdkPopupLayout *layout;
+ GdkRectangle rect;
+ GtkRequisition req;
+
+ gtk_widget_get_preferred_size (widget, NULL, &req);
+ gtk_text_handle_get_padding (handle, &priv->border);
+
+ rect.x = priv->pointing_to.x;
+ rect.y = priv->pointing_to.y + priv->pointing_to.height - priv->border.top;
+ rect.width = req.width - priv->border.left - priv->border.right;
+ rect.height = 1;
+
+ gtk_widget_translate_coordinates (gtk_widget_get_parent (widget),
+ gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW),
rect.x, rect.y, &rect.x, &rect.y);
- gtk_widget_get_allocation (GTK_WIDGET (parent), &allocation);
-
- if (rect.x < 0 || rect.x + rect.width > allocation.width ||
- rect.y < 0 || rect.y + rect.height > allocation.height)
- gtk_widget_set_child_visible (handle_window->widget, FALSE);
- else
- gtk_widget_set_child_visible (handle_window->widget, TRUE);
+ if (priv->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
+ rect.x -= rect.width / 2;
+ else if ((priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
+ (priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
+ gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
+ rect.x -= rect.width;
+
+ layout = gdk_popup_layout_new (&rect,
+ GDK_GRAVITY_SOUTH,
+ GDK_GRAVITY_NORTH);
+ gdk_popup_layout_set_anchor_hints (layout,
+ GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X);
+
+ gdk_popup_present (GDK_POPUP (priv->surface),
+ MAX (req.width, 1),
+ MAX (req.height, 1),
+ layout);
+ gdk_popup_layout_unref (layout);
+
+ gtk_widget_allocate (widget,
+ gdk_surface_get_width (priv->surface),
+ gdk_surface_get_height (priv->surface),
+ -1, NULL);
}
static void
-_gtk_text_handle_update (GtkTextHandle *handle,
- GtkTextHandlePosition pos)
+gtk_text_handle_native_check_resize (GtkNative *native)
{
- GtkTextHandlePrivate *priv;
- HandleWindow *handle_window;
- GtkBorder *border;
-
- priv = handle->priv;
- handle_window = &priv->windows[pos];
- border = &handle_window->border;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (native);
+ GtkWidget *widget = GTK_WIDGET (native);
- if (!priv->parent || !gtk_widget_is_drawable (priv->parent))
- return;
-
- if (handle_window->has_point &&
- handle_window->mode_visible && handle_window->user_visible)
- {
- cairo_rectangle_int_t rect;
- gint width, height;
- GtkWidget *window;
- GtkAllocation alloc;
- gint w, h;
-
- _gtk_text_handle_ensure_widget (handle, pos);
- _gtk_text_handle_get_size (handle, pos, &width, &height);
-
- border->top = height;
- border->bottom = height;
- border->left = width;
- border->right = width;
-
- rect.x = handle_window->pointing_to.x;
- rect.y = handle_window->pointing_to.y + handle_window->pointing_to.height - handle_window->border.top;
- rect.width = width;
- rect.height = 0;
-
- _handle_update_child_visible (handle, pos);
-
- window = gtk_widget_get_parent (handle_window->widget);
- gtk_widget_translate_coordinates (priv->parent, window,
- rect.x, rect.y, &rect.x, &rect.y);
-
- if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
- priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- rect.x -= rect.width / 2;
- else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
- handle_window->dir == GTK_TEXT_DIR_RTL) ||
- (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
- handle_window->dir != GTK_TEXT_DIR_RTL))
- rect.x -= rect.width;
-
- /* The goal is to make the window 3 times as wide and high. The handle
- * will be rendered in the center, making the rest an invisible border.
- * If we hit the edge of the toplevel, we shrink the border to avoid
- * mispositioning the handle, if at all possible. This calculation uses
- * knowledge about how popover_get_rect() works.
- */
-
- gtk_widget_get_allocation (window, &alloc);
-
- w = width + border->left + border->right;
- h = height + border->top + border->bottom;
-
- if (rect.x + rect.width/2 - w/2 < alloc.x)
- border->left = MAX (0, border->left - (alloc.x - (rect.x + rect.width/2 - w/2)));
- if (rect.y + rect.height/2 - h/2 < alloc.y)
- border->top = MAX (0, border->top - (alloc.y - (rect.y + rect.height/2 - h/2)));
- if (rect.x + rect.width/2 + w/2 > alloc.x + alloc.width)
- border->right = MAX (0, border->right - (rect.x + rect.width/2 + w/2 - (alloc.x + alloc.width)));
- if (rect.y + rect.height/2 + h/2 > alloc.y + alloc.height)
- border->bottom = MAX (0, border->bottom - (rect.y + rect.height/2 + h/2 - (alloc.y + alloc.height)));
-
- width += border->left + border->right;
- height += border->top + border->bottom;
-
- gtk_widget_set_size_request (handle_window->widget, width, height);
- gtk_widget_show (handle_window->widget);
- _gtk_window_raise_popover (GTK_WINDOW (window), handle_window->widget);
- _gtk_window_set_popover_position (GTK_WINDOW (window),
- handle_window->widget,
- GTK_POS_BOTTOM, &rect);
- }
- else if (handle_window->widget)
- gtk_widget_hide (handle_window->widget);
+ if (!_gtk_widget_get_alloc_needed (widget))
+ gtk_widget_ensure_allocate (widget);
+ else if (gtk_widget_get_visible (widget))
+ gtk_text_handle_present_surface (handle);
}
static void
-adjustment_changed_cb (GtkAdjustment *adjustment,
- GtkTextHandle *handle)
+gtk_text_handle_native_interface_init (GtkNativeInterface *iface)
{
- _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
- _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
+ iface->get_surface = gtk_text_handle_native_get_surface;
+ iface->get_renderer = gtk_text_handle_native_get_renderer;
+ iface->get_surface_transform = gtk_text_handle_native_get_surface_transform;
+ iface->check_resize = gtk_text_handle_native_check_resize;
}
-static void
-_gtk_text_handle_set_scrollable (GtkTextHandle *handle,
- GtkScrollable *scrollable)
+static gboolean
+surface_render (GdkSurface *surface,
+ cairo_region_t *region,
+ GtkTextHandle *handle)
{
- GtkTextHandlePrivate *priv;
-
- priv = handle->priv;
-
- if (priv->vadj)
- {
- g_signal_handlers_disconnect_by_data (priv->vadj, handle);
- g_clear_object (&priv->vadj);
- }
-
- if (priv->hadj)
- {
- g_signal_handlers_disconnect_by_data (priv->hadj, handle);
- g_clear_object (&priv->hadj);
- }
-
- if (priv->parent_scrollable)
- g_object_remove_weak_pointer (G_OBJECT (priv->parent_scrollable), (gpointer *) &priv->parent_scrollable);
-
- priv->parent_scrollable = scrollable;
-
- if (scrollable)
- {
- g_object_add_weak_pointer (G_OBJECT (priv->parent_scrollable), (gpointer *) &priv->parent_scrollable);
-
- priv->vadj = gtk_scrollable_get_vadjustment (scrollable);
- priv->hadj = gtk_scrollable_get_hadjustment (scrollable);
-
- if (priv->vadj)
- {
- g_object_ref (priv->vadj);
- g_signal_connect (priv->vadj, "changed",
- G_CALLBACK (adjustment_changed_cb), handle);
- g_signal_connect (priv->vadj, "value-changed",
- G_CALLBACK (adjustment_changed_cb), handle);
- }
-
- if (priv->hadj)
- {
- g_object_ref (priv->hadj);
- g_signal_connect (priv->hadj, "changed",
- G_CALLBACK (adjustment_changed_cb), handle);
- g_signal_connect (priv->hadj, "value-changed",
- G_CALLBACK (adjustment_changed_cb), handle);
- }
- }
+ gtk_widget_render (GTK_WIDGET (handle), surface, region);
+ return TRUE;
}
-static void
-_gtk_text_handle_scrollable_notify (GObject *object,
- GParamSpec *pspec,
- GtkTextHandle *handle)
+static gboolean
+surface_event (GdkSurface *surface,
+ GdkEvent *event,
+ GtkTextHandle *handle)
{
- if (pspec->value_type == GTK_TYPE_ADJUSTMENT)
- _gtk_text_handle_set_scrollable (handle, GTK_SCROLLABLE (object));
+ gtk_main_do_event (event);
+ return TRUE;
}
static void
-_gtk_text_handle_update_scrollable (GtkTextHandle *handle,
- GtkScrollable *scrollable)
+surface_mapped_changed (GtkWidget *widget)
{
- GtkTextHandlePrivate *priv;
-
- priv = handle->priv;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- if (priv->parent_scrollable == scrollable)
- return;
-
- if (priv->parent_scrollable && priv->scrollable_notify_id &&
- g_signal_handler_is_connected (priv->parent_scrollable,
- priv->scrollable_notify_id))
- g_signal_handler_disconnect (priv->parent_scrollable,
- priv->scrollable_notify_id);
-
- _gtk_text_handle_set_scrollable (handle, scrollable);
-
- if (priv->parent_scrollable)
- priv->scrollable_notify_id =
- g_signal_connect (priv->parent_scrollable, "notify",
- G_CALLBACK (_gtk_text_handle_scrollable_notify),
- handle);
+ gtk_widget_set_visible (widget, gdk_surface_get_mapped (priv->surface));
}
-static GtkWidget *
-gtk_text_handle_lookup_scrollable (GtkTextHandle *handle)
+static void
+gtk_text_handle_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
{
- GtkTextHandlePrivate *priv;
- GtkWidget *scrolled_window;
-
- priv = handle->priv;
- scrolled_window = gtk_widget_get_ancestor (priv->parent,
- GTK_TYPE_SCROLLED_WINDOW);
- if (!scrolled_window)
- return NULL;
+ GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
- return gtk_bin_get_child (GTK_BIN (scrolled_window));
+ gtk_css_style_snapshot_icon (style,
+ snapshot,
+ gtk_widget_get_width (widget),
+ gtk_widget_get_height (widget));
}
static void
-_gtk_text_handle_parent_hierarchy_changed (GtkWidget *widget,
- GParamSpec *pspec,
- GtkTextHandle *handle)
+gtk_text_handle_realize (GtkWidget *widget)
{
- GtkWidget *toplevel, *scrollable;
- GtkTextHandlePrivate *priv;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
+ GdkSurface *parent_surface;
+ GtkWidget *parent;
- priv = handle->priv;
- toplevel = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+ parent = gtk_widget_get_parent (widget);
+ parent_surface = gtk_native_get_surface (gtk_widget_get_native (parent));
- if (priv->toplevel && !toplevel)
- {
- if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget)
- {
- _gtk_window_remove_popover (GTK_WINDOW (priv->toplevel),
- priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget);
- g_object_unref (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget);
- priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget = NULL;
- }
-
- if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget)
- {
- _gtk_window_remove_popover (GTK_WINDOW (priv->toplevel),
- priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget);
- g_object_unref (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget);
- priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget = NULL;
- }
-
- priv->toplevel = NULL;
- }
+ priv->surface = gdk_surface_new_popup (parent_surface, FALSE);
+ gdk_surface_set_widget (priv->surface, widget);
+
+ g_signal_connect_swapped (priv->surface, "notify::mapped",
+ G_CALLBACK (surface_mapped_changed), widget);
+ g_signal_connect (priv->surface, "render", G_CALLBACK (surface_render), widget);
+ g_signal_connect (priv->surface, "event", G_CALLBACK (surface_event), widget);
- scrollable = gtk_text_handle_lookup_scrollable (handle);
- _gtk_text_handle_update_scrollable (handle, GTK_SCROLLABLE (scrollable));
+ GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->realize (widget);
+
+ priv->renderer = gsk_renderer_new_for_surface (priv->surface);
}
static void
-_gtk_text_handle_set_parent (GtkTextHandle *handle,
- GtkWidget *parent)
+gtk_text_handle_unrealize (GtkWidget *widget)
{
- GtkTextHandlePrivate *priv;
- GtkWidget *scrollable = NULL;
-
- priv = handle->priv;
-
- if (priv->parent == parent)
- return;
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- if (priv->parent && priv->hierarchy_changed_id &&
- g_signal_handler_is_connected (priv->parent, priv->hierarchy_changed_id))
- g_signal_handler_disconnect (priv->parent, priv->hierarchy_changed_id);
+ GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->unrealize (widget);
- priv->parent = parent;
+ gsk_renderer_unrealize (priv->renderer);
+ g_clear_object (&priv->renderer);
- if (parent)
- {
- priv->hierarchy_changed_id =
- g_signal_connect (parent, "notify::root",
- G_CALLBACK (_gtk_text_handle_parent_hierarchy_changed),
- handle);
+ g_signal_handlers_disconnect_by_func (priv->surface, surface_render, widget);
+ g_signal_handlers_disconnect_by_func (priv->surface, surface_event, widget);
+ g_signal_handlers_disconnect_by_func (priv->surface, surface_mapped_changed, widget);
- scrollable = gtk_text_handle_lookup_scrollable (handle);
- }
-
- _gtk_text_handle_update_scrollable (handle, GTK_SCROLLABLE (scrollable));
+ gdk_surface_set_widget (priv->surface, NULL);
+ gdk_surface_destroy (priv->surface);
+ g_clear_object (&priv->surface);
}
static void
-gtk_text_handle_finalize (GObject *object)
+gtk_text_handle_map (GtkWidget *widget)
{
- GtkTextHandlePrivate *priv;
-
- priv = GTK_TEXT_HANDLE (object)->priv;
-
- _gtk_text_handle_set_parent (GTK_TEXT_HANDLE (object), NULL);
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- /* We sank the references, unref here */
- if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget)
- g_object_unref (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].widget);
+ GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->map (widget);
- if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget)
- g_object_unref (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].widget);
-
- G_OBJECT_CLASS (_gtk_text_handle_parent_class)->finalize (object);
+ if (priv->has_point)
+ gtk_text_handle_present_surface (handle);
}
static void
-gtk_text_handle_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+gtk_text_handle_unmap (GtkWidget *widget)
{
- GtkTextHandle *handle;
-
- handle = GTK_TEXT_HANDLE (object);
+ GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- switch (prop_id)
- {
- case PROP_PARENT:
- _gtk_text_handle_set_parent (handle, g_value_get_object (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
+ GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->unmap (widget);
+ gdk_surface_hide (priv->surface);
}
static void
-gtk_text_handle_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+gtk_text_handle_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
{
- GtkTextHandlePrivate *priv;
+ gint size;
- priv = GTK_TEXT_HANDLE (object)->priv;
+ if (orientation == GTK_ORIENTATION_VERTICAL)
+ gtk_text_handle_get_size (GTK_TEXT_HANDLE (widget), NULL, &size);
+ else
+ gtk_text_handle_get_size (GTK_TEXT_HANDLE (widget), &size, NULL);
- switch (prop_id)
- {
- case PROP_PARENT:
- g_value_set_object (value, priv->parent);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
+ *natural = size;
+ *minimum = size;
+ *minimum_baseline = -1;
+ *natural_baseline = -1;
}
static void
-_gtk_text_handle_class_init (GtkTextHandleClass *klass)
+gtk_text_handle_class_init (GtkTextHandleClass *klass)
{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = gtk_text_handle_finalize;
- object_class->set_property = gtk_text_handle_set_property;
- object_class->get_property = gtk_text_handle_get_property;
+ widget_class->snapshot = gtk_text_handle_snapshot;
+ widget_class->realize = gtk_text_handle_realize;
+ widget_class->unrealize = gtk_text_handle_unrealize;
+ widget_class->map = gtk_text_handle_map;
+ widget_class->unmap = gtk_text_handle_unmap;
+ widget_class->measure = gtk_text_handle_measure;
signals[HANDLE_DRAGGED] =
g_signal_new (I_("handle-dragged"),
G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkTextHandleClass, handle_dragged),
+ G_SIGNAL_RUN_LAST, 0,
NULL, NULL,
- _gtk_marshal_VOID__ENUM_INT_INT,
- G_TYPE_NONE, 3,
- GTK_TYPE_TEXT_HANDLE_POSITION,
+ _gtk_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2,
G_TYPE_INT, G_TYPE_INT);
signals[DRAG_STARTED] =
g_signal_new (I_("drag-started"),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL,
NULL,
- G_TYPE_NONE, 1,
- GTK_TYPE_TEXT_HANDLE_POSITION);
+ G_TYPE_NONE, 0, G_TYPE_NONE);
signals[DRAG_FINISHED] =
g_signal_new (I_("drag-finished"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL,
NULL,
- G_TYPE_NONE, 1,
- GTK_TYPE_TEXT_HANDLE_POSITION);
-
- g_object_class_install_property (object_class,
- PROP_PARENT,
- g_param_spec_object ("parent",
- P_("Parent widget"),
- P_("Parent widget"),
- GTK_TYPE_WIDGET,
- GTK_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
+ G_TYPE_NONE, 0, G_TYPE_NONE);
}
static void
-_gtk_text_handle_init (GtkTextHandle *handle)
+handle_drag_begin (GtkGestureDrag *gesture,
+ gdouble x,
+ gdouble y,
+ GtkTextHandle *handle)
{
- handle->priv = _gtk_text_handle_get_instance_private (handle);
-}
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
+ GtkWidget *widget;
-GtkTextHandle *
-_gtk_text_handle_new (GtkWidget *parent)
-{
- return g_object_new (GTK_TYPE_TEXT_HANDLE,
- "parent", parent,
- NULL);
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+ if (priv->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
+ x -= gtk_widget_get_width (widget) / 2;
+ else if ((priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
+ (priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
+ gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
+ x -= gtk_widget_get_width (widget);
+
+ y += priv->border.top / 2;
+
+ priv->dx = x;
+ priv->dy = y;
+ priv->dragged = TRUE;
+ g_signal_emit (handle, signals[DRAG_STARTED], 0);
}
-void
-_gtk_text_handle_set_mode (GtkTextHandle *handle,
- GtkTextHandleMode mode)
+static void
+handle_drag_update (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GtkWidget *widget)
{
- GtkTextHandlePrivate *priv;
- HandleWindow *start, *end;
+ GtkTextHandle *text_handle = GTK_TEXT_HANDLE (widget);
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (text_handle);
+ gdouble start_x, start_y;
+ gint x, y;
- g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
+ gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
- priv = handle->priv;
+ x = priv->pointing_to.x + priv->pointing_to.width / 2 +
+ start_x + offset_x - priv->dx;
+ y = priv->pointing_to.y + priv->pointing_to.height +
+ start_y + offset_y - priv->dy;
- if (priv->mode == mode)
- return;
+ if (priv->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
+ x -= gtk_widget_get_width (widget) / 2;
+ else if ((priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
+ (priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
+ gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
+ x -= gtk_widget_get_width (widget);
+
+ g_signal_emit (widget, signals[HANDLE_DRAGGED], 0, x, y);
+}
+
+static void
+handle_drag_end (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GtkTextHandle *handle)
+{
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
+
+ g_signal_emit (handle, signals[DRAG_FINISHED], 0);
+ priv->dragged = FALSE;
+}
- priv->mode = mode;
- start = &priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START];
- end = &priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END];
+static void
+gtk_text_handle_update_for_role (GtkTextHandle *handle)
+{
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
+ GtkWidget *widget = GTK_WIDGET (handle);
- switch (mode)
+ if (priv->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
{
- case GTK_TEXT_HANDLE_MODE_CURSOR:
- start->mode_visible = FALSE;
- /* end = cursor */
- end->mode_visible = TRUE;
- break;
- case GTK_TEXT_HANDLE_MODE_SELECTION:
- start->mode_visible = TRUE;
- end->mode_visible = TRUE;
- break;
- case GTK_TEXT_HANDLE_MODE_NONE:
- default:
- start->mode_visible = FALSE;
- end->mode_visible = FALSE;
- break;
+ gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_TOP);
+ gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_BOTTOM);
+ gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_INSERTION_CURSOR);
}
-
- if (end->widget)
+ else if (priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END)
{
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
- gtk_style_context_add_class (gtk_widget_get_style_context (end->widget), GTK_STYLE_CLASS_INSERTION_CURSOR);
- else
- gtk_style_context_remove_class (gtk_widget_get_style_context (end->widget), GTK_STYLE_CLASS_INSERTION_CURSOR);
+ gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_TOP);
+ gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_BOTTOM);
+ gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_INSERTION_CURSOR);
}
+ else if (priv->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START)
+ {
+ gtk_widget_add_css_class (widget, GTK_STYLE_CLASS_TOP);
+ gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_BOTTOM);
+ gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_INSERTION_CURSOR);
+ }
+}
- _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
- _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
-
- if (start->widget && start->mode_visible)
- gtk_widget_queue_draw (start->widget);
- if (end->widget && end->mode_visible)
- gtk_widget_queue_draw (end->widget);
+static void
+gtk_text_handle_init (GtkTextHandle *widget)
+{
+ GtkEventController *controller;
+
+ controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
+ g_signal_connect (controller, "drag-begin",
+ G_CALLBACK (handle_drag_begin), widget);
+ g_signal_connect (controller, "drag-update",
+ G_CALLBACK (handle_drag_update), widget);
+ g_signal_connect (controller, "drag-end",
+ G_CALLBACK (handle_drag_end), widget);
+ gtk_widget_add_controller (GTK_WIDGET (widget), controller);
+
+ gtk_text_handle_update_for_role (GTK_TEXT_HANDLE (widget));
}
-GtkTextHandleMode
-_gtk_text_handle_get_mode (GtkTextHandle *handle)
+GtkTextHandle *
+gtk_text_handle_new (GtkWidget *parent)
{
- GtkTextHandlePrivate *priv;
+ GtkWidget *handle;
- g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), GTK_TEXT_HANDLE_MODE_NONE);
+ handle = g_object_new (GTK_TYPE_TEXT_HANDLE,
+ "css-name", I_("cursor-handle"),
+ NULL);
+ gtk_widget_set_parent (handle, parent);
- priv = handle->priv;
- return priv->mode;
+ return GTK_TEXT_HANDLE (handle);
}
void
-_gtk_text_handle_set_position (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GdkRectangle *rect)
+gtk_text_handle_set_role (GtkTextHandle *handle,
+ GtkTextHandleRole role)
{
- GtkTextHandlePrivate *priv;
- HandleWindow *handle_window;
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
- priv = handle->priv;
- pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START);
- handle_window = &priv->windows[pos];
-
- if (priv->mode == GTK_TEXT_HANDLE_MODE_NONE ||
- (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
- pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
+ if (priv->role == role)
return;
- handle_window->pointing_to = *rect;
- handle_window->has_point = TRUE;
+ priv->role = role;
+ gtk_text_handle_update_for_role (handle);
- if (gtk_widget_is_visible (priv->parent))
- _gtk_text_handle_update (handle, pos);
+ if (gtk_widget_get_visible (GTK_WIDGET (handle)))
+ {
+ if (priv->has_point)
+ gtk_text_handle_present_surface (handle);
+ }
}
-void
-_gtk_text_handle_set_visible (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- gboolean visible)
+GtkTextHandleRole
+gtk_text_handle_get_role (GtkTextHandle *handle)
{
- GtkTextHandlePrivate *priv;
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
-
- priv = handle->priv;
- pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START);
-
- priv->windows[pos].user_visible = visible;
+ g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), GTK_TEXT_HANDLE_ROLE_CURSOR);
- if (gtk_widget_is_visible (priv->parent))
- _gtk_text_handle_update (handle, pos);
+ return priv->role;
}
-gboolean
-_gtk_text_handle_get_is_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos)
+void
+gtk_text_handle_set_position (GtkTextHandle *handle,
+ const GdkRectangle *rect)
{
- GtkTextHandlePrivate *priv;
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), FALSE);
+ g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
+
+ if (priv->pointing_to.x == rect->x &&
+ priv->pointing_to.y == rect->y &&
+ priv->pointing_to.width == rect->width &&
+ priv->pointing_to.height == rect->height)
+ return;
- priv = handle->priv;
- pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+ priv->pointing_to = *rect;
+ priv->has_point = TRUE;
- return priv->windows[pos].dragged;
+ if (gtk_widget_is_visible (GTK_WIDGET (handle)))
+ gtk_text_handle_present_surface (handle);
}
-void
-_gtk_text_handle_set_direction (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkTextDirection dir)
+gboolean
+gtk_text_handle_get_is_dragged (GtkTextHandle *handle)
{
- GtkTextHandlePrivate *priv;
+ GtkTextHandlePrivate *priv = gtk_text_handle_get_instance_private (handle);
- g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
-
- priv = handle->priv;
- pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START);
- priv->windows[pos].dir = dir;
+ g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), FALSE);
- if (priv->windows[pos].widget)
- {
- gtk_widget_set_direction (priv->windows[pos].widget, dir);
- _gtk_text_handle_update (handle, pos);
- }
+ return priv->dragged;
}
G_BEGIN_DECLS
-#define GTK_TYPE_TEXT_HANDLE (_gtk_text_handle_get_type ())
-#define GTK_TEXT_HANDLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_TEXT_HANDLE, GtkTextHandle))
-#define GTK_TEXT_HANDLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_TEXT_HANDLE, GtkTextHandleClass))
-#define GTK_IS_TEXT_HANDLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_TEXT_HANDLE))
-#define GTK_IS_TEXT_HANDLE_CLASS(o) (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_TEXT_HANDLE))
-#define GTK_TEXT_HANDLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_TEXT_HANDLE, GtkTextHandleClass))
-
-typedef struct _GtkTextHandle GtkTextHandle;
-typedef struct _GtkTextHandleClass GtkTextHandleClass;
+#define GTK_TYPE_TEXT_HANDLE (gtk_text_handle_get_type ())
+G_DECLARE_FINAL_TYPE (GtkTextHandle, gtk_text_handle,
+ GTK, TEXT_HANDLE, GtkWidget)
typedef enum
{
- GTK_TEXT_HANDLE_POSITION_CURSOR,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START,
- GTK_TEXT_HANDLE_POSITION_SELECTION_END = GTK_TEXT_HANDLE_POSITION_CURSOR
-} GtkTextHandlePosition;
-
-typedef enum
-{
- GTK_TEXT_HANDLE_MODE_NONE,
- GTK_TEXT_HANDLE_MODE_CURSOR,
- GTK_TEXT_HANDLE_MODE_SELECTION
-} GtkTextHandleMode;
-
-struct _GtkTextHandle
-{
- GObject parent_instance;
- gpointer priv;
-};
-
-struct _GtkTextHandleClass
-{
- GObjectClass parent_class;
-
- void (* handle_dragged) (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- gint x,
- gint y);
- void (* drag_finished) (GtkTextHandle *handle,
- GtkTextHandlePosition pos);
-};
+ GTK_TEXT_HANDLE_ROLE_CURSOR,
+ GTK_TEXT_HANDLE_ROLE_SELECTION_START,
+ GTK_TEXT_HANDLE_ROLE_SELECTION_END,
+} GtkTextHandleRole;
-GType _gtk_text_handle_get_type (void) G_GNUC_CONST;
+GtkTextHandle * gtk_text_handle_new (GtkWidget *parent);
-GtkTextHandle * _gtk_text_handle_new (GtkWidget *parent);
+void gtk_text_handle_set_role (GtkTextHandle *handle,
+ GtkTextHandleRole role);
+GtkTextHandleRole gtk_text_handle_get_role (GtkTextHandle *handle);
-void _gtk_text_handle_set_mode (GtkTextHandle *handle,
- GtkTextHandleMode mode);
-GtkTextHandleMode
- _gtk_text_handle_get_mode (GtkTextHandle *handle);
-void _gtk_text_handle_set_position (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GdkRectangle *rect);
-void _gtk_text_handle_set_visible (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- gboolean visible);
+void gtk_text_handle_set_position (GtkTextHandle *handle,
+ const GdkRectangle *rect);
-gboolean _gtk_text_handle_get_is_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos);
-void _gtk_text_handle_set_direction (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkTextDirection dir);
+gboolean gtk_text_handle_get_is_dragged (GtkTextHandle *handle);
G_END_DECLS
typedef struct _GtkTextWindow GtkTextWindow;
typedef struct _GtkTextPendingScroll GtkTextPendingScroll;
+enum
+{
+ TEXT_HANDLE_CURSOR,
+ TEXT_HANDLE_SELECTION_BOUND,
+ TEXT_HANDLE_N_HANDLES
+};
+
struct _GtkTextViewPrivate
{
GtkTextLayout *layout;
gint dnd_x;
gint dnd_y;
- GtkTextHandle *text_handle;
+ GtkTextHandle *text_handles[TEXT_HANDLE_N_HANDLES];
GtkWidget *selection_bubble;
guint selection_bubble_timeout_id;
guint scroll_after_paste : 1;
+ guint text_handles_enabled : 1;
+
/* GtkScrollablePolicy needs to be checked when
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
/* GtkTextHandle handlers */
static void gtk_text_view_handle_drag_started (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
GtkTextView *text_view);
static void gtk_text_view_handle_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
gint x,
gint y,
GtkTextView *text_view);
static void gtk_text_view_handle_drag_finished (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
GtkTextView *text_view);
-static void gtk_text_view_update_handles (GtkTextView *text_view,
- GtkTextHandleMode mode);
+static void gtk_text_view_update_handles (GtkTextView *text_view);
static void gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view);
static void gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view);
gtk_text_view_activate_misc_insert_emoji);
}
+static void
+_gtk_text_view_ensure_text_handles (GtkTextView *text_view)
+{
+ GtkTextViewPrivate *priv = text_view->priv;
+ int i;
+
+ for (i = 0; i < TEXT_HANDLE_N_HANDLES; i++)
+ {
+ if (priv->text_handles[i])
+ continue;
+ priv->text_handles[i] = gtk_text_handle_new (GTK_WIDGET (text_view));
+ g_signal_connect (priv->text_handles[i], "drag-started",
+ G_CALLBACK (gtk_text_view_handle_drag_started), text_view);
+ g_signal_connect (priv->text_handles[i], "handle-dragged",
+ G_CALLBACK (gtk_text_view_handle_dragged), text_view);
+ g_signal_connect (priv->text_handles[i], "drag-finished",
+ G_CALLBACK (gtk_text_view_handle_drag_finished), text_view);
+ }
+}
+
static void
gtk_text_view_init (GtkTextView *text_view)
{
return text_view->priv->selection_node;
}
-static void
-_gtk_text_view_ensure_text_handles (GtkTextView *text_view)
-{
- GtkTextViewPrivate *priv = text_view->priv;
-
- if (priv->text_handle)
- return;
-
- priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (text_view));
- g_signal_connect (priv->text_handle, "drag-started",
- G_CALLBACK (gtk_text_view_handle_drag_started), text_view);
- g_signal_connect (priv->text_handle, "handle-dragged",
- G_CALLBACK (gtk_text_view_handle_dragged), text_view);
- g_signal_connect (priv->text_handle, "drag-finished",
- G_CALLBACK (gtk_text_view_handle_drag_finished), text_view);
-}
-
static void
_gtk_text_view_ensure_magnifier (GtkTextView *text_view)
{
gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
}
- if (priv->text_handle)
- gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE);
+ gtk_text_view_update_handles (text_view);
gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.undo", can_undo);
gtk_widget_action_set_enabled (GTK_WIDGET (text_view), "text.redo", can_redo);
scroll->yalign,
TRUE);
- if (text_view->priv->text_handle)
- gtk_text_view_update_handles (text_view,
- _gtk_text_handle_get_mode (text_view->priv->text_handle));
+ gtk_text_view_update_handles (text_view);
free_pending_scroll (scroll);
if (priv->magnifier)
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (priv->magnifier), NULL);
+ g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_CURSOR], gtk_widget_unparent);
+ g_clear_pointer ((GtkWidget **) &priv->text_handles[TEXT_HANDLE_SELECTION_BOUND], gtk_widget_unparent);
+
GTK_WIDGET_CLASS (gtk_text_view_parent_class)->destroy (widget);
}
if (priv->magnifier_popover)
gtk_widget_destroy (priv->magnifier_popover);
- if (priv->text_handle)
- g_object_unref (priv->text_handle);
g_object_unref (priv->im_context);
g_free (priv->im_module);
if (priv->popup_menu)
gtk_native_check_resize (GTK_NATIVE (priv->popup_menu));
+
+ if (priv->text_handles[TEXT_HANDLE_CURSOR])
+ gtk_native_check_resize (GTK_NATIVE (priv->text_handles[TEXT_HANDLE_CURSOR]));
+
+ if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
+ gtk_native_check_resize (GTK_NATIVE (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
}
static void
}
static void
-gtk_text_view_set_handle_position (GtkTextView *text_view,
- GtkTextIter *iter,
- GtkTextHandlePosition pos)
+gtk_text_view_set_handle_position (GtkTextView *text_view,
+ GtkTextHandle *handle,
+ GtkTextIter *iter)
{
GtkTextViewPrivate *priv;
GdkRectangle rect;
x = rect.x - priv->xoffset;
y = rect.y - priv->yoffset;
- if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
+ if (!gtk_text_handle_get_is_dragged (handle) &&
(x < 0 || x > SCREEN_WIDTH (text_view) ||
y < 0 || y > SCREEN_HEIGHT (text_view)))
{
/* Hide the handle if it's not being manipulated
* and fell outside of the visible text area.
*/
- _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
+ gtk_widget_hide (GTK_WIDGET (handle));
}
else
{
GtkTextDirection dir = GTK_TEXT_DIR_LTR;
GtkTextAttributes attributes = { 0 };
- _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
+ gtk_widget_show (GTK_WIDGET (handle));
rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view));
rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view));
_text_window_to_widget_coords (text_view, &rect.x, &rect.y);
- _gtk_text_handle_set_position (priv->text_handle, pos, &rect);
+ gtk_text_handle_set_position (handle, &rect);
if (gtk_text_iter_get_attributes (iter, &attributes))
dir = attributes.direction;
- _gtk_text_handle_set_direction (priv->text_handle, pos, dir);
+ gtk_widget_set_direction (GTK_WIDGET (handle), dir);
}
}
}
static void
-gtk_text_view_handle_dragged (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- gint x,
- gint y,
- GtkTextView *text_view)
+gtk_text_view_handle_dragged (GtkTextHandle *handle,
+ gint x,
+ gint y,
+ GtkTextView *text_view)
{
GtkTextViewPrivate *priv;
- GtkTextIter old_cursor, old_bound;
- GtkTextIter cursor, bound, iter;
- GtkTextIter *min, *max;
- GtkTextHandleMode mode;
+ GtkTextIter cursor, bound, iter, *old_iter;
GtkTextBuffer *buffer;
- GtkTextHandlePosition cursor_pos;
priv = text_view->priv;
buffer = get_buffer (text_view);
- mode = _gtk_text_handle_get_mode (handle);
_widget_to_text_surface_coords (text_view, &x, &y);
gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
x + priv->xoffset,
y + priv->yoffset);
- gtk_text_buffer_get_iter_at_mark (buffer, &old_cursor,
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
gtk_text_buffer_get_insert (buffer));
- gtk_text_buffer_get_iter_at_mark (buffer, &old_bound,
+ gtk_text_buffer_get_iter_at_mark (buffer, &bound,
gtk_text_buffer_get_selection_bound (buffer));
- cursor = old_cursor;
- bound = old_bound;
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
- gtk_text_iter_compare (&cursor, &bound) >= 0)
- {
- cursor_pos = GTK_TEXT_HANDLE_POSITION_CURSOR;
- max = &cursor;
- min = &bound;
- }
- else
- {
- cursor_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
- max = &bound;
- min = &cursor;
- }
- if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
{
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
- gtk_text_iter_compare (&iter, min) <= 0)
+ /* Avoid running past the other handle in selection mode */
+ if (gtk_text_iter_compare (&iter, &bound) >= 0 &&
+ gtk_widget_is_visible (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])))
{
- iter = *min;
- gtk_text_iter_forward_char (&iter);
+ iter = bound;
+ gtk_text_iter_backward_char (&iter);
}
- *max = iter;
- gtk_text_view_set_handle_position (text_view, &iter, pos);
+ old_iter = &cursor;
+ gtk_text_view_set_handle_position (text_view, handle, &iter);
}
- else
+ else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
{
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
- gtk_text_iter_compare (&iter, max) >= 0)
+ /* Avoid running past the other handle */
+ if (gtk_text_iter_compare (&iter, &cursor) <= 0)
{
- iter = *max;
- gtk_text_iter_backward_char (&iter);
+ iter = cursor;
+ gtk_text_iter_forward_char (&iter);
}
- *min = iter;
- gtk_text_view_set_handle_position (text_view, &iter, pos);
+ old_iter = &bound;
+ gtk_text_view_set_handle_position (text_view, handle, &iter);
}
+ else
+ g_assert_not_reached ();
- if (gtk_text_iter_compare (&old_cursor, &cursor) != 0 ||
- gtk_text_iter_compare (&old_bound, &bound) != 0)
+ if (gtk_text_iter_compare (&iter, old_iter) != 0)
{
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+ *old_iter = iter;
+
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR] &&
+ !gtk_widget_is_visible (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])))
gtk_text_buffer_place_cursor (buffer, &cursor);
else
gtk_text_buffer_select_range (buffer, &cursor, &bound);
- if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos))
+ if (handle == priv->text_handles[TEXT_HANDLE_CURSOR])
{
text_view->priv->cursor_handle_dragged = TRUE;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_insert (buffer));
}
- else
+ else if (handle == priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
{
text_view->priv->selection_handle_dragged = TRUE;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_selection_bound (buffer));
}
+
+ gtk_text_view_update_handles (text_view);
}
- if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos))
- gtk_text_view_show_magnifier (text_view, &cursor, x, y);
- else
- gtk_text_view_show_magnifier (text_view, &bound, x, y);
+ gtk_text_view_show_magnifier (text_view, &iter, x, y);
}
static void
-gtk_text_view_handle_drag_started (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkTextView *text_view)
+gtk_text_view_handle_drag_started (GtkTextHandle *handle,
+ GtkTextView *text_view)
{
text_view->priv->cursor_handle_dragged = FALSE;
text_view->priv->selection_handle_dragged = FALSE;
}
static void
-gtk_text_view_handle_drag_finished (GtkTextHandle *handle,
- GtkTextHandlePosition pos,
- GtkTextView *text_view)
+gtk_text_view_handle_drag_finished (GtkTextHandle *handle,
+ GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
extend_selection (text_view, SELECT_WORDS, &cursor, &start, &end);
gtk_text_buffer_select_range (buffer, &start, &end);
- gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
+ gtk_text_view_update_handles (text_view);
}
else
gtk_text_view_selection_bubble_popup_set (text_view);
static gboolean cursor_visible (GtkTextView *text_view);
static void
-gtk_text_view_update_handles (GtkTextView *text_view,
- GtkTextHandleMode mode)
+gtk_text_view_update_handles (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
- GtkTextIter cursor, bound, min, max;
+ GtkTextIter cursor, bound;
GtkTextBuffer *buffer;
- buffer = get_buffer (text_view);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
- gtk_text_buffer_get_insert (buffer));
- gtk_text_buffer_get_iter_at_mark (buffer, &bound,
- gtk_text_buffer_get_selection_bound (buffer));
-
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
- gtk_text_iter_compare (&cursor, &bound) == 0)
- {
- mode = GTK_TEXT_HANDLE_MODE_CURSOR;
- }
-
- if (mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
- (!gtk_widget_is_sensitive (GTK_WIDGET (text_view)) || !cursor_visible (text_view)))
+ if (!priv->text_handles_enabled)
{
- mode = GTK_TEXT_HANDLE_MODE_NONE;
- }
-
- _gtk_text_handle_set_mode (priv->text_handle, mode);
-
- if (gtk_text_iter_compare (&cursor, &bound) >= 0)
- {
- min = bound;
- max = cursor;
+ if (priv->text_handles[TEXT_HANDLE_CURSOR])
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
+ if (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND])
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
}
else
{
- min = cursor;
- max = bound;
- }
+ _gtk_text_view_ensure_text_handles (text_view);
+ buffer = get_buffer (text_view);
- if (mode != GTK_TEXT_HANDLE_MODE_NONE)
- gtk_text_view_set_handle_position (text_view, &max,
- GTK_TEXT_HANDLE_POSITION_SELECTION_END);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &bound,
+ gtk_text_buffer_get_selection_bound (buffer));
- if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
- gtk_text_view_set_handle_position (text_view, &min,
- GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+ if (gtk_text_iter_compare (&cursor, &bound) == 0 && priv->editable)
+ {
+ /* Cursor mode */
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
+
+ gtk_text_view_set_handle_position (text_view,
+ priv->text_handles[TEXT_HANDLE_CURSOR],
+ &cursor);
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
+ GTK_TEXT_HANDLE_ROLE_CURSOR);
+ gtk_widget_show (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
+ }
+ else if (gtk_text_iter_compare (&cursor, &bound) != 0)
+ {
+ /* Selection mode */
+ gtk_text_view_set_handle_position (text_view,
+ priv->text_handles[TEXT_HANDLE_CURSOR],
+ &cursor);
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_CURSOR],
+ GTK_TEXT_HANDLE_ROLE_SELECTION_START);
+ gtk_widget_show (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
+
+ gtk_text_view_set_handle_position (text_view,
+ priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
+ &bound);
+ gtk_text_handle_set_role (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND],
+ GTK_TEXT_HANDLE_ROLE_SELECTION_END);
+ gtk_widget_show (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
+ }
+ else
+ {
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_CURSOR]));
+ gtk_widget_hide (GTK_WIDGET (priv->text_handles[TEXT_HANDLE_SELECTION_BOUND]));
+ }
+ }
}
static gboolean
gtk_text_view_reset_blink_time (text_view);
gtk_text_view_pend_cursor_blink (text_view);
- if (priv->text_handle)
- _gtk_text_handle_set_mode (priv->text_handle,
- GTK_TEXT_HANDLE_MODE_NONE);
+ text_view->priv->text_handles_enabled = FALSE;
+ gtk_text_view_update_handles (text_view);
gtk_text_view_selection_bubble_popup_unset (text_view);
}
else if (button == GDK_BUTTON_PRIMARY)
{
- GtkTextHandleMode handle_mode = GTK_TEXT_HANDLE_MODE_NONE;
gboolean extends = FALSE;
GdkModifierType state;
*/
GtkTextIter start, end;
- if (is_touchscreen)
- handle_mode = GTK_TEXT_HANDLE_MODE_CURSOR;
+ priv->text_handles_enabled = is_touchscreen;
get_iter_from_gesture (text_view, GTK_GESTURE (gesture),
&iter, NULL, NULL);
!gtk_widget_get_visible (priv->selection_bubble))
{
gtk_text_view_selection_bubble_popup_set (text_view);
- handle_mode = GTK_TEXT_HANDLE_MODE_NONE;
+ priv->text_handles_enabled = FALSE;
}
else
{
gtk_text_view_selection_bubble_popup_unset (text_view);
- handle_mode = GTK_TEXT_HANDLE_MODE_SELECTION;
}
}
else
}
case 2:
case 3:
- if (is_touchscreen)
- {
- handle_mode = GTK_TEXT_HANDLE_MODE_SELECTION;
- break;
- }
gtk_text_view_end_selection_drag (text_view);
get_iter_from_gesture (text_view, GTK_GESTURE (gesture),
break;
}
- _gtk_text_view_ensure_text_handles (text_view);
- gtk_text_view_update_handles (text_view, handle_mode);
+ gtk_text_view_update_handles (text_view);
}
if (n_press >= 3)
text_view);
gtk_text_view_selection_bubble_popup_unset (text_view);
- if (priv->text_handle)
- _gtk_text_handle_set_mode (priv->text_handle,
- GTK_TEXT_HANDLE_MODE_NONE);
+ text_view->priv->text_handles_enabled = FALSE;
+ gtk_text_view_update_handles (text_view);
if (priv->editable)
{
gpointer data)
{
GtkTextView *text_view = data;
- GtkTextViewPrivate *priv = text_view->priv;
- if (priv->text_handle)
- gtk_text_view_update_handles (text_view,
- _gtk_text_handle_get_mode (priv->text_handle));
+ gtk_text_view_update_handles (text_view);
}
static void
if (is_touchscreen)
{
- _gtk_text_view_ensure_text_handles (text_view);
- gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
+ text_view->priv->text_handles_enabled = TRUE;
+ gtk_text_view_update_handles (text_view);
gtk_text_view_show_magnifier (text_view, &cursor, x, y);
}
}
if (!is_touchscreen && clicked_in_selection &&
!gtk_drag_check_threshold (GTK_WIDGET (text_view), start_x, start_y, x, y))
{
- GtkTextHandleMode mode = GTK_TEXT_HANDLE_MODE_NONE;
GtkTextIter iter;
/* Unselect everything; we clicked inside selection, but
gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
gtk_text_view_check_cursor_blink (text_view);
- if (priv->text_handle)
- {
- if (is_touchscreen)
- mode = GTK_TEXT_HANDLE_MODE_CURSOR;
-
- gtk_text_view_update_handles (text_view, mode);
- }
+ gtk_text_view_update_handles (text_view);
}
}
*/
gtk_text_view_update_im_spot_location (text_view);
- if (priv->text_handle)
- gtk_text_view_update_handles (text_view,
- _gtk_text_handle_get_mode (priv->text_handle));
+ gtk_text_view_update_handles (text_view);
if (priv->anchored_children.length > 0)
gtk_widget_queue_allocate (GTK_WIDGET (text_view));
if (need_reset)
{
gtk_text_view_reset_im_context (text_view);
- if (text_view->priv->text_handle)
- gtk_text_view_update_handles (text_view,
- _gtk_text_handle_get_mode (text_view->priv->text_handle));
+ gtk_text_view_update_handles (text_view);
has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), NULL, NULL);
gtk_css_node_set_visible (text_view->priv->selection_node, has_selection);
GtkTextView *text_view)
{
gboolean visible;
- GtkTextHandle *handle;
- GtkTextHandleMode mode;
visible = gtk_widget_get_visible (popover);
-
- handle = text_view->priv->text_handle;
- mode = _gtk_text_handle_get_mode (handle);
-
- if (!visible)
- gtk_text_view_update_handles (text_view, mode);
- else
- {
- _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, FALSE);
- _gtk_text_handle_set_visible (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, FALSE);
- }
+ text_view->priv->text_handles_enabled = !visible;
+ gtk_text_view_update_handles (text_view);
}
static void